package com.fengwk.support.util;

import java.lang.reflect.Array;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Set;

import com.fengwk.support.exception.BaseException;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.internal.LinkedTreeMap;


/**
 * json工具
 */
public class JsonUtils {

	private final static GsonBuilder GSON_BUILDER;
	
	private final static int DEFAULT_GSON_COUNT = 10;
	
	// Boolean true为使用 false为未使用
	private final static Map<Gson, Boolean> GSON_POOL;

	static {
		GSON_POOL = new ConcurrentHashMap<Gson, Boolean>();
		GSON_BUILDER = new GsonBuilder();
		GSON_BUILDER.disableHtmlEscaping();// 关闭HtmlEscaping
		GSON_BUILDER.setDateFormat(DateUtils.DEFAULT);
		LongSerializer longSerializer = new LongSerializer();
		GSON_BUILDER.registerTypeAdapter(Long.class, longSerializer);
		GSON_BUILDER.registerTypeAdapter(long.class, longSerializer);
		for(int i = 0; i < DEFAULT_GSON_COUNT; i ++) {
			GSON_POOL.put(json(), false);
		}
	}
	
	private JsonUtils() {
		throw new BaseException("can not instance " + getClass());
	}

	/**
	 * 获取gson对象
	 * 
	 * @return
	 */
	public static Gson json() {
		return GSON_BUILDER.create();
	}
	
	public static <T> T parseLinkedTreeMap(LinkedTreeMap<?, ?> map, Class<T> clazz) {
		return fromJson(toJson(map), clazz);
	}
	
	public static <G> List<G> parseLinkedTreeMapToList(Collection<?> collection, Class<G> generic) {
		return fromJsonList(toJson(collection), generic);
	}
	
	/**
	 * json的反序列化至List
	 * 
	 * @param json 数组类型的json字符串
	 * @param generic 泛型
	 * @return
	 */
	public static <G> List<G> fromJsonList(String json, Class<G> generic) {
		if(json == null || "null".equals(json) || "\"null\"".equals(json))
			return null;
			
		List<G> list = new ArrayList<>();
		Gson gson = borrowGson();
        JsonArray arry = new JsonParser().parse(json).getAsJsonArray();
        if(arry != null) {
        	for(JsonElement jsonElement : arry)
        		list.add(gson.fromJson(jsonElement, generic));  
        }
        returnGson(gson);
		return list;
	}
	
	/**
	 * json的反序列化至Set
	 * 
	 * @param json 数组类型的json字符串
	 * @param generic 泛型
	 * @return
	 */
	public static <G> Set<G> fromJsonSet(String json, Class<G> generic) {
		Set<G> set = null;
		List<G> list = fromJsonList(json, generic);
		if (list != null) {
			set = new LinkedHashSet<>();
			set.addAll(list);
		}
		return set;
	}
	
	/**
	 * json的反序列化至Set
	 * 
	 * @param json 数组类型的json字符串
	 * @param generic 泛型
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <G> G[] fromJsonArray(String json, Class<G> generic) {
		G[] arr = null;
		List<G> list = fromJsonList(json, generic);
		if (list != null) {
			arr = (G[]) Array.newInstance(generic, list.size());
			list.toArray(arr);
		}
		return arr;
	}
	
	/**
	 * json的反序列化
	 * 
	 * @param json json数据
	 * @param clazz 字节码
	 * @return
	 */
	public static <T> T fromJson(String json, Class<T> clazz) {
		Gson gson = borrowGson();
		T resObj = gson.fromJson(json, clazz);
		returnGson(gson);
		return resObj;
	}
	
	/**
	 * json的反序列化
	 * 
	 * @param json
	 * @param typeOfT {@link TypeToken}
	 * @return
	 */
	public static <T> T fromJson(String json, Type typeOfT) {
		Gson gson = borrowGson();
		T resObj = gson.fromJson(json, typeOfT);
		returnGson(gson);
		return resObj;
	}
	
	/**
	 * json的序列化
	 * 
	 * @param o 需要序列化的对象
	 * @return
	 */
	public static String toJson(Object o) {
		Gson gson = borrowGson();
		String resStr = gson.toJson(o);
		returnGson(gson);
		return resStr;
	}
	
	// 租借gson对象
	private static Gson borrowGson() {
		Iterator<Entry<Gson, Boolean>> gsonIt = GSON_POOL.entrySet().iterator();
		while(gsonIt.hasNext()) {
			Entry<Gson, Boolean> entry = gsonIt.next();
			if(entry.getValue() == false) {
				entry.setValue(true);
				return entry.getKey();
			}
		}
		return json();
	}
	
	// 归还gson对象
	private static void returnGson(Gson gson) {
		if(GSON_POOL.get(gson) != null) 
			GSON_POOL.put(gson, false);
	}
	
	/**
	 * 适配long类型,long类型将转换为字符串,解决long类型在js中的精度丢失
	 * 
	 * @author fengwk
	 * @version 创建时间:2017年10月31日 下午8:12:35
	 */
	private static class LongSerializer implements JsonSerializer<Long> {
		@Override
		public JsonElement serialize(Long src, Type typeOfSrc, JsonSerializationContext context) {
            if(src != null) {
                String strSrc = src.toString();
                if(strSrc.length() > 15)
                	 return new JsonPrimitive(strSrc);
            }
            return new JsonPrimitive(src);
        }
    }

}